package com.kamistoat.meimeistore.product.service.impl;

import com.kamistoat.common.constant.ProductConstant;
import com.kamistoat.meimeistore.product.dao.AttrAttrgroupRelationDao;
import com.kamistoat.meimeistore.product.dao.AttrGroupDao;
import com.kamistoat.meimeistore.product.dao.CategoryDao;
import com.kamistoat.meimeistore.product.entity.AttrAttrgroupRelationEntity;
import com.kamistoat.meimeistore.product.entity.AttrGroupEntity;
import com.kamistoat.meimeistore.product.entity.CategoryEntity;
import com.kamistoat.meimeistore.product.service.AttrAttrgroupRelationService;
import com.kamistoat.meimeistore.product.service.CategoryService;
import com.kamistoat.meimeistore.product.vo.AttrGroupRelationVo;
import com.kamistoat.meimeistore.product.vo.AttrResponseVo;
import com.kamistoat.meimeistore.product.vo.AttrVo;
import com.kamistoat.meimeistore.product.entity.AttrEntity;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kamistoat.common.utils.PageUtils;
import com.kamistoat.common.utils.Query;

import com.kamistoat.meimeistore.product.dao.AttrDao;
import com.kamistoat.meimeistore.product.service.AttrService;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;


@Service("attrService")
public class AttrServiceImpl extends ServiceImpl<AttrDao, AttrEntity> implements AttrService {

    @Autowired
    AttrAttrgroupRelationService attrAttrgroupRelationService;
    @Autowired
    AttrGroupDao attrGroupDao;
    @Autowired
    AttrAttrgroupRelationDao attrAttrgroupRelationDao;
    @Autowired
    CategoryDao categoryDao;
    @Autowired
    CategoryService categoryService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<AttrEntity> page = this.page(
                new Query<AttrEntity>().getPage(params),
                new QueryWrapper<AttrEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 如果传入的categoryId非零，则只查询catelogId下对应的属性。
     * 如果是零，就查询所有的属性。
     * 根据type的值决定查询基础属性还是销售属性
     *
     * @param params     分页属性
     * @param categoryId 指定查询三级菜单Id
     * @param type       属性类别，sale：销售属性。base：基础属性
     * @return 分页包装
     */
    @Override
    public PageUtils queryPage(Map<String, Object> params, Long categoryId, String type) {
        // 首先要获取包含在分页属性中的查询关键字，两种查询情况下，都要根据key进行模糊查询
        String key = (String) params.get("key");
        // 确定查询的属性类别。0是销售属性，1是基本属性
        Integer attrType = "base".equalsIgnoreCase(type) ?
                ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode() : ProductConstant.AttrEnum.ATTR_TYPE_SALE.getCode();
        // 定义一个List，保存直接查询的结果，然后为这个List添加剩下的字段
        List<AttrEntity> attrEntityList;
        // 定义一个空QueryWrapper，仅有一个查询销售/基本属性的条件。根据不同的情况再放置不同的查询条件
        QueryWrapper<AttrEntity> queryWrapper = new QueryWrapper<AttrEntity>().eq("attr_type", attrType);
        // 规定当没有点击菜单时，查询全部基础属性，要求此时前端传categoryId = 0
        // 因此categoryId != 0时，就要多加一个catelogId的判断条件
        if (categoryId != 0) {
            queryWrapper.eq("catelog_id", categoryId);
        }
        // 如果查询关键字key不为空，就继续添加条件
        // 其实加不加这个判断完全不影响，因为即便key是空，.eq/.like都能合法进行
        if (!StringUtils.isEmpty(key)) {
            // 因为这里的逻辑是：{菜单id相等} and {属性分组id相等 or 属性分组名称相似 or 属性分组描述相似}
            // 所以在.and()中写一个箭头函数，来包含进所有的or逻辑
            // or逻辑由于都是同级的，所以直接一直用.or()往后叠加就可以
            queryWrapper.and((obj) -> {
                obj.eq("attr_id", key).or().like("attr_name", key).or().like("value_select", key);
            });
        }
        // 对不同情况的pageData进行封装查询
        IPage<AttrEntity> pageData = this.page(new Query<AttrEntity>().getPage(params), queryWrapper);
        attrEntityList = pageData.getRecords();  // 先把查询结果提取出来，此时的结果List中全是AttrEntity
        // 使用流处理，来对整个AttrEntity列表中的元素进行处理
        List<AttrResponseVo> responseVos = attrEntityList.stream().map((attrEntity) -> {
            // 把每一个AttrEntity的字段先copy到AttrResponseVo中
            AttrResponseVo attrResponseVo = new AttrResponseVo();
            BeanUtils.copyProperties(attrEntity, attrResponseVo);
            /**
             * 设置属性分组的名字
             * 当然，也是只有基础属性才做，销售属性没有属性分组，直接跳过
             */
            if ("base".equalsIgnoreCase(type)) {
                // 首先根据每一个attrEntity主键，去attrAttrgroupRelationDao中查出所在属性分组的id
                AttrAttrgroupRelationEntity attrAttrgroupRelationEntity =
                        attrAttrgroupRelationDao.selectOne(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrEntity.getAttrId()));
                // 注意这里要判断一下attrAttrgroupRelationEntity.getAttrGroupId()是否为空，因为销售属性没有所属分组信息。
                if (attrAttrgroupRelationEntity != null && attrAttrgroupRelationEntity.getAttrGroupId() != null) {
                    AttrGroupEntity attrGroupEntity = attrGroupDao.selectById(attrAttrgroupRelationEntity.getAttrGroupId());
                    attrResponseVo.setGroupName(attrGroupEntity.getAttrGroupName());
                }
            }
            /**
             * 设置菜单名称
             */
            // 先根据菜单Id查到categoryEntity，然后获取菜单名
            CategoryEntity categoryEntity = categoryDao.selectById(attrEntity.getCatelogId());
            if (categoryEntity != null) {
                attrResponseVo.setCatelogName(categoryEntity.getName());
            }
            return attrResponseVo;
        }).collect(Collectors.toList());
        PageUtils pageUtils = new PageUtils(pageData);  // 先把原先的查询结果放进pageUtils中
        pageUtils.setList(responseVos);  // 然后把分页查询原来的结果（只有AttrEntity）替换掉（换成新的AttrResponseVo）。
        // 不能用pageData.setRecord进行替换，因为pageData强制要求必须是AttrEntity列表
        return pageUtils;
    }

    /**
     * 接收前端发送的AttrVo类，处理成AttrEntity后再调用原生的save方法保存
     * 注意这里同样涉及到两个操作，保存AttrEntity和保存AttrAttrGroupRelationEntity
     *
     * @param attrVo 前端发送的AttrVo类
     */
    @Transactional
    @Override
    public void saveAttrVo(AttrVo attrVo) {
        // 首先保存AttrEntity
        AttrEntity attrEntity = new AttrEntity();
        // 直接调用spring提供的方法复制Entity，当然前提条件是两者的属性名称相同
        BeanUtils.copyProperties(attrVo, attrEntity);
        // 最后调用原生保存Entity的方法
        this.save(attrEntity);

        // 然后保存AttrAttrGroupRelationEntity
        AttrAttrgroupRelationEntity attrAttrgroupRelationEntity = new AttrAttrgroupRelationEntity();
        attrAttrgroupRelationEntity.setAttrGroupId(attrVo.getAttrGroupId());  // 属性分组的Id从前端传来的数据中提取
        // 注意，这个方法是新增基础属性时调用的，前端传来的AttrVo中的AttrId是空，所以不能从AttrVo中获取AttrId。
        // 而是应该利用自增主键的attrEntity，自动生成的AttrId来获取AttrID
        attrAttrgroupRelationEntity.setAttrId(attrEntity.getAttrId());
        attrAttrgroupRelationService.save(attrAttrgroupRelationEntity);
        // TODO  同样要考虑冗余字段更新的问题
    }

    /**
     * 将三级菜单路径一同返回
     *
     * @param attrId 基础属性Id
     * @return AttrResponseVo
     */
    @Override
    public AttrResponseVo getAttrInfo(Long attrId) {
        AttrResponseVo attrResponseVo = new AttrResponseVo();
        AttrEntity attrEntity = this.getById(attrId);
        BeanUtils.copyProperties(attrEntity, attrResponseVo);
        // 查属性分组Id
        // 销售属性没有属性分组，所以也不用查了，只返回菜单路径即可
        if (attrEntity.getAttrType().equals(ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode())) {
            AttrAttrgroupRelationEntity attrAttrgroupRelationEntity =
                    attrAttrgroupRelationDao.selectOne(new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrEntity.getAttrId()));
            if (attrAttrgroupRelationEntity != null) {
                attrResponseVo.setAttrGroupId(attrAttrgroupRelationEntity.getAttrGroupId());
            }
        }
        // 查菜单路径，直接用之前写好的CategoryService中的方法就可以了
        Long[] catelogPath = categoryService.findCatelogPath(attrEntity.getCatelogId());
        // 更新vo
        attrResponseVo.setCatelogPath(catelogPath);
        return attrResponseVo;
    }

    /**
     * 前端修改基础属性传来的AttrVo
     *
     * @param attrVo 前端的AttrVo
     */
    @Transactional
    @Override
    public void updateAttrVo(AttrVo attrVo) {
        //  首先更新AttrEntity
        AttrEntity attrEntity = new AttrEntity();
        BeanUtils.copyProperties(attrVo, attrEntity);
        this.updateById(attrEntity);
        // 更新AttrAttrGroupRelation
        // 由于销售属性在更改时，根本不会提供属性分组选择，所以这里一定是不会进入if的
        if (attrVo.getAttrGroupId() != null) {
            // 存在三种情况：
            // ①：基础属性，relation数据表中存在与其attrId对应的行数据，且groupId非空
            // ②：基础属性，但是其relation关系被属性分组解除，relation表中对应该attrId的行数据不存在
            // ③：销售属性更改为基础属性，relation表中存在对应attrId的行数据，且groupId为空
            // ①③情况为update，②情况为save
            // 先从关系表中把对应AttrId的属性查出来
            AttrAttrgroupRelationEntity attrAttrgroupRelationEntity =
                    attrAttrgroupRelationDao.selectOne(
                            new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrVo.getAttrId()));
            // ①③的情况与②分开处理
            if (attrAttrgroupRelationEntity != null) {
                // 修改其属性分组Id
                attrAttrgroupRelationEntity.setAttrGroupId(attrVo.getAttrGroupId());
                // 再更新
                attrAttrgroupRelationDao.update(attrAttrgroupRelationEntity,
                        new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrVo.getAttrId()));
            } else {
                AttrAttrgroupRelationEntity temp = new AttrAttrgroupRelationEntity();
                temp.setAttrId(attrVo.getAttrId());
                temp.setAttrGroupId(attrVo.getAttrGroupId());
                attrAttrgroupRelationService.save(temp);
            }
        }
    }

    /**
     * 接收一个属性分组Id，返回这个属性分组关联的所有属性
     *
     * @param attrgroupId 属性分组iD
     * @return AttrEntity列表
     */
    @Override
    public List<AttrEntity> getAttrRelation(Long attrgroupId) {
        // 注意怎样直接用库方法匹配查询多个值，用Dao接口selectList(new QueryWrapper.条件)
        List<AttrAttrgroupRelationEntity> attrAttrgroupRelationEntityList =
                attrAttrgroupRelationDao.selectList(
                        new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_group_id", attrgroupId));
        // 如果这个属性分组下还没有关联任何属性，那么attrAttrgroupRelationEntityList就是一个空列表，直接返回一个空列表
        // 最好不要返回null，和前端会产生不兼容
        if (attrAttrgroupRelationEntityList.size() == 0) {
            return Collections.emptyList();  // 新建一个空列表
        } else {
            // 制作一个List，保存所有的AttrId
            // 再次强调如何用流处理提取出List中各个元素的某个字段
            // 其实就是lambda表达式的复杂写法
            List<Long> attrIdList = attrAttrgroupRelationEntityList.stream().map((entity) -> {
                return entity.getAttrId();
            }).collect(Collectors.toList());
            // 最后这个collect转List千万不要忘记
            // 直接用Service层集成好的listByIds(id列表)，可以快速查出整个List
            List<AttrEntity> attrEntityList = this.listByIds(attrIdList);
            return attrEntityList;
        }
    }

    /**
     * 根据前端发送的数据，删除与属性分组关联的关联关系
     *
     * @param relationVos 列表，元素都是AttrGroupRelationVo
     */
    @Override
    public void deleteRelation(List<AttrGroupRelationVo> relationVos) {
        // 同样是使用流处理，先根据attrId和attrGroupId把Relation的id查出来，构造成列表
        List<Long> idsList = relationVos.stream().map((relationVo) -> {
            return attrAttrgroupRelationDao.selectOne(new QueryWrapper<AttrAttrgroupRelationEntity>().
                    eq("attr_id", relationVo.getAttrId()).
                    eq("attr_group_id", relationVo.getAttrGroupId())).getId();
        }).collect(Collectors.toList());
        // 直接用deleteBatchIds删除
        System.out.println(idsList);
        attrAttrgroupRelationDao.deleteBatchIds(idsList);
    }

    /**
     * 查询无关联的基础属性
     * 在方法中调用服务层的getNoRelation(attrgroupId,params)方法，并实现它。
     * 思路是：先从pms_attr_group中查出attrgroupId的Entity的catelog_id值X，然后从pms_attr表中查出所有catelog_id等于X的基础属性，
     * 再从pms_attr_attrgroup_relation表中删除掉attr_group_id非空的
     *
     * @param attrgroupId 属性分组Id
     * @param params      分页查询参数
     * @return 分页查询结果
     */
    @Override
    public PageUtils getNoRelation(Long attrgroupId, Map<String, Object> params) {
        Long catelogId = attrGroupDao.selectOne(
                new QueryWrapper<AttrGroupEntity>().eq("attr_group_id", attrgroupId)).getCatelogId();
        // 只要本三级菜单下的基础属性
        QueryWrapper<AttrEntity> wrapper = new QueryWrapper<AttrEntity>().
                eq("catelog_id", catelogId).
                eq("attr_type", ProductConstant.AttrEnum.ATTR_TYPE_BASE.getCode());
        // 查询关键字
        String key = (String) params.get("key");
        if (!StringUtils.isEmpty(key)) {
            wrapper.and((obj -> {
                obj.eq("attr_id", key).or().like("attr_name", key);
            }));
        }
        IPage<AttrEntity> pageData = this.page(
                new Query<AttrEntity>().getPage(params), wrapper);
        List<AttrEntity> attrEntityList = pageData.getRecords();
        List<AttrEntity> attrNoRelation = attrEntityList.stream().map((attrEntity) -> {
            // 在updateAttrVo中我们讲过，只要是没有关联分组的基础属性有两种情况：
            // ①、在relation数据表中根本就不存在行数据
            // ②、存在数据但groupId字段为空值
            // 所以除了这两种情况，都返回null
            if (attrAttrgroupRelationDao.selectOne(
                    new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrEntity.getAttrId()))
                    == null) {
                return attrEntity;
            } else if (attrAttrgroupRelationDao.selectOne(
                    new QueryWrapper<AttrAttrgroupRelationEntity>().eq("attr_id", attrEntity.getAttrId())).getAttrGroupId()
                    == null) {
                return attrEntity;
            } else {
                return null;
            }
        }).collect(Collectors.toList());
        while (attrNoRelation.remove(null)) ;  // 删除掉列表中的null
        PageUtils pageUtils = new PageUtils(pageData);  // 先把原先的查询结果放进pageUtils中
        pageUtils.setList(attrNoRelation);  // 然后把分页查询原来的结果（全部AttrEntity）替换掉（换成没有关联关系的AttrEntity）。
        return pageUtils;
    }

}